#include "stdafx.h"
#include "Graph.h"
#include "PrintUtils.h"
//#include "PrintUtils.h"

namespace GraphTools
{

	//string bool2str(const bool c)
	//{
	//	return c? "true" : "false";
	//}

	//string digit2str(const int i)
	//{
	//	switch (i)
	//	{
	//	case 0: 
	//		return "0";
	//	case 1: 
	//		return "1";
	//	case 2: 
	//		return "2";
	//	case 3: 
	//		return "3";
	//	case 4: 
	//		return "4";
	//	case 5: 
	//		return "5";
	//	case 6: 
	//		return "6";
	//	case 7: 
	//		return "7";
	//	case 8: 
	//		return "8";
	//	case 9: 
	//		return "9";
	//	default: 
	//		return "oops";
	//	}
	//}

	//string int2str(const int i)
	//{
	//	if (i == 0)
	//		return "0";
	//	string out = "";
	//	int c = abs(i);
	//	while (c != 0)
	//	{
	//		out = digit2str(c % 10) + out;
	//		//if (c == c / 10)
	//		//	break;
	//		c = c / 10;
	//	}
	//	return i > 0 ? out : "-" + out;
	//}
	
	Scaffold::Scaffold(string _Name)
		:Name(_Name), Contigs(), MinEdge(), weight(0), NonMinEdges(), Ciclic(false) {}

	Scaffold::Scaffold(Vertex* _start)
		:Name(""), Contigs(), MinEdge(), weight(0), NonMinEdges(), Ciclic(false) 
	{
		if (_start->GetDegree() == 0)
		{
			Contigs.push_back(_start);
			return;
		}
		list<Edge*> edges = list<Edge*>();
		list<Vertex*> ites = list<Vertex*>();
		ites.push_back(_start);
		int cicle = 0; //if = 2 cicle

		while (ites.size() != 0)
		{
			auto terminal = ites.front();
			terminal->SetFlag(true);
			ites.pop_front();
			auto actual_edges = terminal->GetEdges();
			cicle = 0;
			for (auto edge_ = actual_edges.begin(); edge_ != actual_edges.end(); ++edge_)
			{
				if ((*edge_)->GetFlag())
				{
					cicle++;
					continue;
				}
				Vertex* other = (*edge_)->GetOther(*terminal)._Get();
				ites.push_back(other);
				(*edge_)->SetFlag(true);
				edges.push_back((*edge_).get());
			}
		}
		
		if (cicle != 2)
		{
			this->Ciclic = false;
			Vertex *endpath;
			for (auto ite = edges.begin(); ite != edges.end(); ++ite)
			{
				auto terminals = (*ite)->GetTerminals();
				if (terminals.front()._Get()->GetDegree() == 1)
				{
					endpath = terminals.front()._Get();
				}
				else if (terminals.back()._Get()->GetDegree() == 1)
				{
					endpath = terminals.back()._Get();
				}
				(*ite)->SetFlag(false);
				this->weight += (*ite)->GetWeight();
			}

			this->Contigs.push_back(endpath);
			auto edge = endpath->GetEdges().front().get();
			edge->SetFlag(true);
			auto other = edge->GetOther(*endpath)._Get();

			while (other->GetDegree() == 2)
			{
				this->Contigs.push_back(other);
				edge = other->GetEdges().front().get();
				if (edge->GetFlag())
				{
					edge = other->GetEdges().back().get();
				}
				edge->SetFlag(true);
				other = edge->GetOther(*other)._Get();
			}
			this->NonMinEdges = edges;
			this->Contigs.push_back(other);

			//if (Contigs.size() > 4)
				//cout<<"";
			
			//cout << "found aciclic " << endpath->GetName() <<"\n";
		}
		else
		{
			//basta eliminare FISICAMENTE il minimo e applicare sopra.
			//minW diventa ptr<Edge> e poi lo inserisco
			//elimino il minore e riapplico sopra
			this->Ciclic = true;
			auto minEdge = edges.front();
			auto ite = edges.begin();
			ite++;
			for (; ite != edges.end(); ++ite)
			{
				if (minEdge->GetWeight() > (*ite)->GetWeight())
				{
					this->weight += minEdge->GetWeight();
					NonMinEdges.push_back(minEdge);
					minEdge = (*ite);
				}
				else
				{
					NonMinEdges.push_back(*ite);
					this->weight += (*ite)->GetWeight();
				}
			}

			minEdge->SetFlag(true);

			auto min_e = minEdge->GetTerminals().front()._Get()->GetSecondEdge();

			this->MinEdge = min_e;

			MinEdge._Get()->DestroyEdge();

			Vertex *endpath = min_e->GetTerminals().front()._Get();

			this->Contigs.push_back(endpath);
			auto edge = endpath->GetEdges().front().get();
			edge->SetFlag(true);
			auto other = edge->GetOther(*endpath)._Get();

			while (other->GetDegree() == 2)
			{
				this->Contigs.push_back(other);
				edge = other->GetEdges().front().get();
				if (edge->GetFlag())
				{
					edge = other->GetEdges().back().get();
				}
				edge->SetFlag(true);
				other = edge->GetOther(*other)._Get();
			}
			//this->NonMinEdges = edges;
			this->Contigs.push_back(other);
			
			//auto tmp = new Edge(Contigs.front(), Contigs.back(), min_e->GetWeight());

			//min_e->ReGen();


			//if (Contigs.size() > 3)
			//	cout<<"";

			/*this->Contigs.push_back(endpath);
			auto edge = endpath->GetEdges().front().get();
			if (edge->GetFlag())
			{
				edge = endpath->GetEdges().back().get();
			}
			edge->SetFlag(true);
			auto other = edge->GetOther(*endpath)._Get();

			while (other->GetDegree() == 2)
			{
				this->Contigs.push_back(other);
				edge = other->GetEdges().front().get();
				if (edge->GetFlag())
				{
					edge = other->GetEdges().back().get();
				}
				if (edge->GetFlag())
					break;
				edge->SetFlag(true);
				other = edge->GetOther(*other)._Get();
			}
			this->NonMinEdges = edges;
			this->Contigs.push_back(other);*/

			//cout << "found ciclic\n" << endpath->GetName() <<"\n";

		}

	}

	void Scaffold::RenameOrdinal(int i)
	{
		this->Name = PrintUtils::int2str(i);
	}

	//void Scaffold::CreateFrom(Vertex* _start)
	//{
	//	//ptr<Scaffold> res(this);
	//	if (ordinal_name == 156)
	//		cout << "halt";
	//	cout << "da finire\n";

	//	if (_start->GetFlag())
	//		return;

	//	if (_start->GetDegree() <= 0)
	//	{
	//		return;
	//	}

	//	list<Vertex*> terminals = list<Vertex*>();
	//	
	//	queue<Edge*> to_visit = queue<Edge*>();

	//	if (_start->GetDegree() == 1)
	//	{
	//		terminals.push_back(_start);
	//		to_visit.push(_start->GetNotVisited().front());
	//	}
	//	else
	//	{
	//		auto edges = _start->GetNotVisited();
	//		this->Contigs.push_back(_start);
	//		to_visit.push(edges.front());
	//		to_visit.push(edges.back());
	//	}

	//	_start->SetFlag(true);

	//	Edge* ite = to_visit.front();

	//	while (to_visit.size() > 0)
	//	{
	//		ite = to_visit.front();
	//		to_visit.pop();
	//		ite->SetFlag(true);
	//		this->weight  += ite->GetWeight();

	//		if (this->MinEdge == NULL)
	//		{
	//			this->MinEdge = ite;
	//		}
	//		else if (ite->GetWeight() < this->MinEdge->GetWeight())
	//		{
	//			this->NonMinEdges.push_back(this->MinEdge);
	//			this->MinEdge = ite;
	//		}
	//		else
	//		{
	//			this->NonMinEdges.push_back(ite);
	//		}
	//		
	//		Vertex* other = ite->GetNotVisited();

	//		if (other == NULL)
	//		{
	//			cout << "find ciclic path\n";
	//			break;
	//		}

	//		other->SetFlag(true);

	//		if (other->GetDegree() == 1)
	//		{
	//			terminals.push_back(other);
	//			cout << "find aciclic path\n";				
	//			continue;
	//		}
	//		this->Contigs.push_back(other);
	//		to_visit.push(other->GetNotVisited().front());
	//	}

	//	if (terminals.size() == 2)
	//	{
	//		this->Contigs.push_front(terminals.front());
	//		this->Contigs.push_back(terminals.back());
	//		this->Ciclic = false;
	//		this->Name = int2str(ordinal_name++);
	//	}
	//	else
	//	{
	//		auto ts = this->MinEdge->GetTerminals();
	//		//du know why error 
	//		this->Contigs.remove_if([=](Vertex* v){return *v == *ts.front()._Get() || *v == *ts.back()._Get();});
	//		this->Contigs.push_front(ts.front()._Get());
	//		this->Contigs.push_back(ts.back()._Get());
	//		this->weight -= this->MinEdge->GetWeight();
	//		this->Ciclic = true;
	//		this->Name = int2str(ordinal_name++);
	//	}
	//}

	int Scaffold::GetWeight()
	{
		return this->weight;
	}

	const bool Scaffold::operator< (Scaffold& e)
	{
		return ((*this).GetWeight()) < (e.GetWeight());
	}

	/*void Scaffold::SortPath()
	{
		for (auto ite = Contigs.begin(); ite != Contigs.end(); ++ite)
		{
			(*ite)->SetFlag(false);
		}

		auto start = this->Contigs.front();
		Contigs.clear();
		auto end = this->MinEdge->GetTerminals().front();
		if (*start == *end._Get())
		{
			end = this->MinEdge->GetTerminals().back();
		}
		Contigs.push_back(start);
		start->SetFlag(true);

		while (*start != *end._Get())
		{
			auto tmp = start->GetNeighbours().front();
			if (tmp._Get()->GetFlag())
			{
				tmp = start->GetNeighbours().back();
			}
			Contigs.push_back(tmp._Get());
			start = tmp._Get();
		}
		Contigs.push_back(start);
	}*/

	bool Contains(list<weak_ptr<Vertex>> neighbours, Vertex *elem)
	{
		for (auto it = neighbours.begin(); it != neighbours.end(); ++it)
		{
			auto neigh = (*it)._Get();
			if (*neigh == *elem)
				return true;
		}
		return false;
	}

	bool Scaffold::Check()
	{
		auto contigs = this->Contigs;
		auto ite = contigs.front();
		contigs.pop_front();
		while (contigs.size() > 0)
		{
			auto next = contigs.front();
			contigs.pop_front();
			if (!Contains(ite->GetNeighbours(), next))
			{
				return false;
			}
			ite = next;
		}
	}

	string Scaffold::ToString()
	{
		//std::stringstream s;
		if (!this->Check())
		{
			//this->SortPath();
			if (!this->Check())
			{
				throw "error, inconsistent scaffold";
			}
		}
		// name tab peso tab contig blank ...
		
		string out = "";
		out += this->Name + "\t" + PrintUtils::int2str(this->weight) + "\t" + PrintUtils::bool2str(this->Ciclic) + "\t";

		for (auto ite = this->Contigs.begin(); ite != this->Contigs.end(); ++ite)
		{
			out += (*ite)->GetName() + " ";
		}
		return out + "\n";
	}

}